using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using PuppeteerSharp;
using Polly;

namespace PddWebAuto.Helper
{
    public class NetworkHelper
    {
        public static async Task<bool> LoadAndWaitForAsync(IPage page, Process process,
            string url, int timeOut, int retryAttempts = 3)
        {
            if (retryAttempts < 0) return false;
            var bakUrl = url.Clone().ToString();
            try
            {
                if (page == null || page.IsClosed)
                    return false;
                var nav = page.WaitForNavigationAsync(new NavigationOptions() { Timeout = timeOut });
                if (page.Url == bakUrl)
                {
                    await page.ReloadAsync(new NavigationOptions() { Timeout = timeOut });
                }
                else
                {
                    //转到页面
                    await page.GoToAsync(url, timeOut,
                        new[]
                        {
                            WaitUntilNavigation.Load, WaitUntilNavigation.DOMContentLoaded,
                            WaitUntilNavigation.Networkidle0
                        });
                }

                //等待导航完成
                await nav;
                await TaskHelper.Delay(3000);
                return true;
            }
            catch (TaskCanceledException)
            {
                return false;
            }
            catch (Exception)
            {
                var retry = retryAttempts - 1;

                //刷新代理
                await RefreshProxyAsync(process);

                //再次加载
                return await LoadAndWaitForAsync(page, process, bakUrl, timeOut, retry);
            }
        }

        /// <summary>
        /// 刷新代理
        /// </summary>
        /// <param name="process"></param>
        public static async Task RefreshProxyAsync(Process process)
        {
            try
            {
                if (process == null || process.HasExited)
                    return;
                var args = process.StartInfo.Arguments;
                var proxy = "--proxy-server=";
                if (args.Contains(proxy))
                {
                    var sIndex = args.IndexOf(":", args.IndexOf(proxy, StringComparison.Ordinal),
                        StringComparison.Ordinal);
                    var eIndex = args.IndexOf(" ", sIndex, StringComparison.Ordinal);
                    var port = args.Substring(sIndex + 1, eIndex - sIndex - 1);
                    if (int.TryParse(port, out _))
                    {
                        //刷新下代理
                        await TunnelProxiesHelper.RefreshProxyAsync(port);
                    }
                }
            }
            catch (Exception)
            {
                // ignored
            }
        }

        /// <summary>
        /// 等待网络
        /// </summary>
        /// <param name="page"></param>
        /// <param name="process"></param>
        /// <param name="timeOut"></param>
        /// <param name="faildExecute"></param>
        /// <param name="retryAttempts"></param>
        /// <returns></returns>
        public static async Task<bool> WaitForNetworkIdleAsync(IPage page, Process process, int timeOut,
            Func<Task> faildExecute, int retryAttempts = 1)
        {
            if (retryAttempts < 0) return false;
            try
            {
                if (page == null || page.IsClosed)
                    return false;
                await page.WaitForNavigationAsync(new NavigationOptions { Timeout = timeOut });
                return true;
            }
            catch (Exception)
            {
                var retry = retryAttempts - 1;
                if (retry < 0)
                {
                    return false;
                }
                //切换代理
                await RefreshProxyAsync(process);
                try
                {
                    if (faildExecute != null)
                        await faildExecute.Invoke();
                }
                catch (Exception)
                {
                    // ignored
                }
                return await WaitForNetworkIdleAsync(page, process, timeOut, faildExecute, retry);
            }
        }

        public static async Task<bool> WaitSelector(IPage page, string selector, int timeOut,
            Func<Task> faildExecute, int retryAttempts = 3)
        {
            try
            {
                if (page == null || page.IsClosed)
                    return false;
                var element =
                    await page.WaitForSelectorAsync(selector, new WaitForSelectorOptions { Timeout = timeOut });
                return element != null;
            }
            catch (Exception)
            {
                var retry = retryAttempts - 1;
                if (retryAttempts < 0) return false;
                if (faildExecute != null)
                {
                    try
                    {
                        await faildExecute.Invoke();
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                }

                return await WaitSelector(page, selector, timeOut, faildExecute, retry);
            }
        }
    }
}